package com.xiweicheng.test.proxy;

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;

import com.google.common.collect.Maps;
import com.xiweicheng.test.proxy.doc.handler.ADocHandler;
import com.xiweicheng.test.proxy.doc.handler.DocClient;
import com.xiweicheng.test.proxy.doc.handler.IframeDocHandler;
import com.xiweicheng.test.proxy.doc.handler.ImgDocHandler;
import com.xiweicheng.test.proxy.doc.handler.InjectDocHandler;
import com.xiweicheng.test.proxy.doc.handler.ScriptDocHandler;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Utils {

	public static String toProxy(String target, String baseUrl, String basePath) {

		try {

			URL url = new URL(target);
			// 创建代理服务器
			InetSocketAddress addr = new InetSocketAddress("localhost", 8080);
			Proxy proxy = new Proxy(Proxy.Type.HTTP, addr); // http 代理
			// 如果我们知道代理server的名字, 可以直接使用
			URLConnection conn = url.openConnection(proxy);
			InputStream in = conn.getInputStream();
			StringBuffer out = new StringBuffer();
			byte[] b = new byte[4096];
			for (int n; (n = in.read(b)) != -1;) {
				out.append(new String(b, 0, n));
			}

			// System.out.println(out.toString());
			return inject(out.toString(), baseUrl, basePath);

		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}

	public static String inject(String html, String baseUrl, String basePath) {

		DocClient client = new DocClient();

		return client.addHandlers(
				// inject script
				new InjectDocHandler(),
				// tag:a
				new ADocHandler(),
				// tag:img
				new ImgDocHandler(),
				// tag:iframe
				new IframeDocHandler(),
				// tag:script
				new ScriptDocHandler()).invoke(html, baseUrl, basePath).html();
	}

	private static Map<String, Cookie> readCookieMap(HttpServletRequest request) {
		Map<String, Cookie> cookieMap = new HashMap<String, Cookie>();
		Cookie[] cookies = request.getCookies();
		if (null != cookies) {
			for (Cookie cookie : cookies) {
				cookieMap.put(cookie.getName(), cookie);
			}
		}
		return cookieMap;
	}

	public static Cookie getCookie(HttpServletRequest request, String name) {
		Map<String, Cookie> cookieMap = readCookieMap(request);
		if (cookieMap.containsKey(name)) {
			Cookie cookie = (Cookie) cookieMap.get(name);
			return cookie;
		} else {
			return null;
		}
	}

	public static void addCookie(HttpServletResponse response, String name, String value, int maxAge) {
		Cookie cookie = new Cookie(name, value);
		cookie.setPath("/");
		if (maxAge > 0) {
			cookie.setMaxAge(maxAge);
		}
		response.addCookie(cookie);
	}

	public static String joinPaths(String... paths) {

		StringBuffer sb = new StringBuffer();

		for (int i = 0; i < paths.length; i++) {
			String p = paths[i];
			if (StringUtils.isEmpty(p)) {
				continue;
			}
			if (p.startsWith(".")) {
				p = p.substring(1);
			}
			if (i != 0) {
				if (!p.startsWith("/")) {
					sb.append("/");
				}
			}
			if (p.endsWith("/")) {
				p = p.substring(0, p.length() - 1);
			}
			sb.append(p);
		}

		return sb.toString();
	}

	// eg: /a/b/c.html -> /a/b
	public static String getBasePath(String path) {

		if (!StringUtils.isEmpty(path)) {

			String[] paths = path.split("/");
			List<String> list = new ArrayList<>();
			if (paths.length > 1) {
				for (int i = 0; i < paths.length - 1; i++) {
					list.add(paths[i]);
				}

				return StringUtils.join(list, "/");
			}
		}

		return null;
	}

	public static void setRespHeader(HttpServletResponse resp, Map<String, List<String>> headerFields) {

		List<String> headers = Arrays.asList("keep-alive", "accept-ranges", "cache-control", "server", "etag",
				"location", "connection", /* "expires", */ "last-modified", /* "content-length", */ "date",
				"content-type", "p3p", "set-cookie"
		/* "transfer-encoding" */);

		// Header & Cookie handle
		for (String name : headerFields.keySet()) {
			// log.debug("Conn Resp Header Name: {}", name);
			if (StringUtils.isEmpty(name) || !headers.contains(name.toLowerCase())) {
				continue;
			}
			List<String> list = headerFields.get(name);
			for (String value : list) {

				log.debug("Conn resp Header: {}={}", name, value);

				if (Constants.HEADER_SET_COOKIE.equalsIgnoreCase(name)) {
					String cookie = value;
					log.debug("Header Set-Cookie: {}", cookie);
					if (StringUtils.isNotEmpty(cookie)) {
						String[] arr = cookie.split(";");
						String[] arr2 = arr[0].trim().split("=");
						Utils.addCookie(resp, arr2[0], arr2[1], 3600 * 24);
					}
				} else if (Constants.HEADER_LOCATION.equalsIgnoreCase(name)) {
					resp.setHeader(name, "http://localhost/?__target__=" + value); // TODO
				} else {
					resp.setHeader(name, value);
				}
			}
		}
	}

	// public static void setReqHeader(HttpServletRequest req, HttpURLConnection
	// conn) {
	//
	// List<String> headers = Arrays.asList("cookie", "content-type", "connection",
	// "user-agent", "accept",
	// /** "accept-encoding", **/
	// "accept-language", /* "content-length", */ "host", "origin", "referer",
	// "upgrade-insecure-requests");
	//
	// Enumeration<String> headerNames = req.getHeaderNames();
	// while (headerNames.hasMoreElements()) {
	// String name = (String) headerNames.nextElement();
	// if (headers.contains(name.toLowerCase()) &&
	// StringUtils.isNotEmpty(req.getHeader(name))) {
	// log.debug("Request Header: {}={}", name, req.getHeader(name));
	// conn.setRequestProperty(name, req.getHeader(name));
	// }
	// }
	// }

	public static Map<String, String> getHeaders(HttpServletRequest req) {

		List<String> headers = Arrays.asList("cookie", "content-type", "connection", "user-agent", "accept",
				/** "accept-encoding", **/
				"accept-language", /* "content-length", */ "host", "origin", "referer", "upgrade-insecure-requests");

		HashMap<String, String> headersMap = Maps.newHashMap();

		Enumeration<String> headerNames = req.getHeaderNames();
		while (headerNames.hasMoreElements()) {
			String name = (String) headerNames.nextElement();
			if (headers.contains(name.toLowerCase()) && StringUtils.isNotEmpty(req.getHeader(name))) {
				log.debug("Request Header: {}={}", name, req.getHeader(name));
				headersMap.put(name, req.getHeader(name));
			}
		}

		return headersMap;
	}

	public static void setReqHeader(Map<String, String> headers, HttpURLConnection conn) {

		if (headers == null) {
			return;
		}

		headers.forEach((k, v) -> {
			conn.setRequestProperty(k, v);
		});

	}
}
